/*
Copyright (c) 2009-2010, Warwick Warp Limited
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Warwick Warp Limited nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL WARWICK WARP LIMITED BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef COMMON_WARP_ARRAY_STORAGE_H_INCLUDED
#define COMMON_WARP_ARRAY_STORAGE_H_INCLUDED

#include <complex>

#if !(defined(_MSC_VER) || defined(__APPLE__))
#include <malloc.h>
#endif

namespace warp {

// Win32
#ifdef _MSC_VER
template<typename T>
T *new_aligned(size_t alignment, size_t element_count)
{
		return reinterpret_cast<T*>(_aligned_malloc(element_count*sizeof(T),alignment));
}
template<typename T>
void delete_aligned(T *ptr)
{
	_aligned_free(ptr);
}
// Mac OS X 
#elif defined(__APPLE__) 
template<typename T>
T *new_aligned(size_t alignment, size_t element_count)
{
	return reinterpret_cast<T*>(malloc(element_count*sizeof(T)));
}
template<typename T>
void delete_aligned(T *ptr)
{
	free(ptr);
}
// Linux
#else
/*! 
	\brief Allocate space for element_count elements aligned on the alignment boundary

	NB: Element constructors are not currently called.
	\todo Call constructors?
*/
template<typename T>
T *new_aligned(size_t alignment, size_t element_count)
{
	return reinterpret_cast<T*>(memalign(alignment,element_count*sizeof(T)));
}
/*!
	\brief Release memory previously allocated with new_aligned
 
	NB: Element destructors are not currently called
	\todo Call destructors?
*/
template<typename T>
void delete_aligned(T *ptr)
{
	free(ptr);
}
#endif

template<typename T>
void delete_aligned(const T *ptr)
{
	// Don't try and delete const pointers you fool!
	T *dummy = ptr;
}
	
/*!
	\brief Allocates storage for an array with new[] and delete[]
*/
template<typename ELEMENT>
class array_storage
{
public:
	typedef ELEMENT value_type;
	
	//! \brief Storage for n elements
	array_storage(int n)
	{
		m_data = new ELEMENT[n];
	}
	//! \brief Releases memory
	~array_storage()
	{
		delete[] m_data;
		m_data = NULL;
	}
	//! \brief Obtain access to the aray
	operator ELEMENT*() const
	{
		return m_data;
	}
	
	ELEMENT* store() const
	{
		return m_data;
	}
	
private:
	array_storage(const array_storage&);
	array_storage& operator=(const array_storage&);
	ELEMENT *m_data; //!< Pointer to the allocated memory
};

/*!
	\brief Allocates storage for an array aligned on the specifed boundary
	
	Note that constructors and destructors are not called for the objects!
 */
template<typename ELEMENT, int ALIGNMENT=16>
class aligned_storage
{
public:
	typedef ELEMENT value_type;
	
	//! \brief Storage for n elements
	aligned_storage(int n)
	{
		m_data = new_aligned<ELEMENT>(ALIGNMENT,n);
	}
	//! \brief Releases memory
	~aligned_storage()
	{
		delete_aligned(m_data);
		m_data = NULL;
	}
	//! \brief Obtain access to the aray
	operator ELEMENT*() const
	{
		return m_data;
	}
	
	ELEMENT* store() const
	{
		return m_data;
	}
	
private:
	aligned_storage(const aligned_storage&);
	aligned_storage& operator=(const aligned_storage&);
	ELEMENT *m_data; //!< Pointer to the allocated memory
};

template<>
class array_storage<float> : public aligned_storage<float,16>
{
public:
	array_storage(int n) : aligned_storage<float,16>(n) {}
};

template<>
class array_storage<double> : public aligned_storage<double,16>
{
public:
	array_storage(int n) : aligned_storage<double,16>(n) {}
};

template<>
class array_storage< std::complex<float> > : public aligned_storage< std::complex<float> ,16>
{
public:
	array_storage(int n) : aligned_storage< std::complex<float>, 16>(n) {}
};

template<>
class array_storage< std::complex<double> > : public aligned_storage< std::complex<double> ,16>
{
public:
	array_storage(int n) : aligned_storage< std::complex<double>,16 >(n) {}
};

} // namespace

#endif
